/* eslint-disable no-console */

import { noise } from '@chainsafe/libp2p-noise'
import { yamux } from '@chainsafe/libp2p-yamux'
// import { floodsub } from '@libp2p/floodsub'
// import { gossipsub } from '@chainsafe/libp2p-gossipsub'
import { gossipsub } from '../gossipsub/index.js'
import { mplex } from '@libp2p/mplex'
import { tcp } from '@libp2p/tcp'
import { webSockets } from '@libp2p/websockets'
import { createLibp2p } from 'libp2p'
import { identifyService } from 'libp2p/identify'
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
import { seedHexToSeed, peerIdToDid, createEd25519PeerId } from '../peer-id.js'
import { floodsub } from '@libp2p/floodsub'
import Logger from 'logplease'
import pMap from 'p-map';

const logger = Logger.create('server')
Logger.setLogLevel('DEBUG')

const SEED_HEX = '5110e63bf075b89770deb88d89281526d5c858380e76db257a86834bb42d2085'

const createNode = async (name) => {
  const seed = seedHexToSeed(SEED_HEX)
  const peerId = await createEd25519PeerId(seed)
  const node = await createLibp2p({
    peerId,
    addresses: {
      listen: ['/ip4/0.0.0.0/tcp/4008/ws']
    },
    transports: [webSockets()],
    streamMuxers: [yamux(), mplex()],
    connectionEncryption: [noise()],
    services: {
      // pubsub: gossipsub({ emitSelf: false, allowPublishToZeroPeers: true,  }),
      pubsub: floodsub(),
      identify: identifyService()
    },
    connectionManager: {
      minConnections: 500,
      maxConnections: 1000
    }
  })

  logger.debug(`${name}: started with id ${node.peerId.toString()}`)
  // logger.debug('Listening on:')
  // node.getMultiaddrs().forEach((ma) => logger.debug(ma.toString()))

  // node.services.pubsub.subscribe(topic)
  // node.services.pubsub.addEventListener('message', (evt) => {
  //   logger.debug(`${name} received: ${uint8ArrayToString(evt.detail.data)} on topic ${evt.detail.topic}`)
  // })

  return node
}

const waitForChannelReady = async (node, did, name) => {
  node.services.pubsub.subscribe(`${did}-channel`)
  let waitIntervalId_2
  const waitChannelReady = () => {
    return new Promise((resolve, reject) => {
      logger.debug(`wait for channal ${did} is ready...`)
      waitIntervalId_2 = setInterval(() => {
        node.services.pubsub.subscribe(`${did}-channel`)
        const peers = node.services.pubsub.getSubscribers(`${did}-channel`)
        // logger.debug(`channel ${did} peers`, peers.map(item => item.toString()))
        if (peers.length > 0) {
          logger.debug(`channel ${did} is ready!!!!!`)
          resolve()
        }
      }, 1000)
    })
  }
  await waitChannelReady()
  clearInterval(waitIntervalId_2)
}

const createServer = async (name, hbCount) => {
  const node = await createNode(name)

  node.addEventListener('peer:connect', async (evt) => {
    let peerId = evt.detail.toString()
    let did = peerIdToDid(peerId)
    logger.debug('connected to %s', peerId) // Log connected peer
    
    // waitForChannelReady(node, did, 'server')
    logger.debug('subscribe channel: ', `${did}-channel`)
    node.services.pubsub.subscribe(`${did}-channel`)

    let connections = node.components.connectionManager.connections
    logger.debug('connections', connections.size)

    if (connections.size == hbCount) {
      logger.debug('start beart beat')
      let body = {
        type: 'start-heart-beat'
      }
      connections.map.forEach((value, key) => {
        logger.debug(key)
        let peerId = key
        console.log(peerId)
        let did = peerIdToDid(peerId)
        node.services.pubsub.publish(`${did}-channel`, uint8ArrayFromString(JSON.stringify(body)))
      })
    }

    
  })

  // node.services.pubsub.addEventListener('subscription-change', (evt) => {
  //   const msg = evt.detail
  //   logger.debug('receive subscription', msg.subscriptions.length)
  //   for (let sub of msg.subscriptions) {
  //     let { topic, subscribe } = sub
  //     // logger.debug('subscribing topic: ', topic)
  //     // node.services.pubsub.subscribe(topic)
  //   }
  // })

  node.addEventListener('peer:disconnect', (evt) => {
    let peerId = evt.detail.toString()

    let did = peerIdToDid(peerId)
    logger.debug(`Disconnected from libp2p peer ${peerId}`)

    logger.debug('unsubscribe member channel', did)
    node.services.pubsub.unsubscribe(`${did}-channel`)
  })

  node.services.pubsub.addEventListener('message', async (evt) => {
    const topic = evt.detail.topic
    const from = evt.detail.from.toString()
    const msg = uint8ArrayToString(evt.detail.data)
    const body = JSON.parse(msg)

    logger.debug(`server received: ${msg}`)

    // let connections = node.components.connectionManager.connections
    // logger.debug('connections', connections.size)

    if (topic.endsWith('-channel')) {
      const channelDid = topic.split('-')[0]
      const body = JSON.parse(msg)
      
      if (body.type == 'heart-beat') {
        node.services.pubsub.publish(`${channelDid}-channel`, uint8ArrayFromString(JSON.stringify({
          type: 'heart-beat-res',
          name: 'server'
        })))
      }
    }
  })
  return node
}

;(async () => {

  const server = await createServer('server', 400)

  logger.debug('Listening on:')
  server.getMultiaddrs().forEach((ma) => logger.debug(ma.toString()))

})()
